package com.xzzz.irda.guardcat.server.metric;

import cn.hutool.core.collection.CollUtil;
import com.xzzz.common.base.util.thread.NamedThreadFactory;
import com.xzzz.irda.guardcat.server.app.AppManager;
import com.xzzz.irda.guardcat.server.app.pojo.MachineInfo;
import com.xzzz.irda.guardcat.server.GuardcatServerProperties;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.web.context.WebServerInitializedEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;

import java.util.List;
import java.util.Set;
import java.util.concurrent.*;

/**
 * 从机器拉取监控数据
 *
 * @author leyou
 */
@Slf4j
@Component
public class MetricFetcher implements ApplicationListener<WebServerInitializedEvent> {

    @Autowired
    private AppManager appManager;

    /**
     * 定时线程，用于定时创建拉取任务
     */
    private final ScheduledExecutorService fetchScheduleService = Executors.newScheduledThreadPool
            (1, new NamedThreadFactory("guardcat-server-metrics-fetch-task"));

    /**
     * 用于拉取一个APP
     */
    private final ExecutorService fetchAppService;

    /**
     * 用于拉取一个APP下的某一个指标项
     */
    private final ExecutorService fetchMachineWorker;

    /**
     * 服务配置
     */
    private final GuardcatServerProperties serverProperties;

    /**
     * 构建指标拉取任务
     */
    public MetricFetcher(GuardcatServerProperties serverProperties) {
        this.serverProperties = serverProperties;
        int cores = Runtime.getRuntime().availableProcessors() * 4;
        long keepAliveTime = 0;
        int queueSize = 2048;
        RejectedExecutionHandler handler = new ThreadPoolExecutor.DiscardPolicy();
        fetchAppService = new ThreadPoolExecutor(cores, cores,
                keepAliveTime, TimeUnit.MILLISECONDS, new ArrayBlockingQueue<>(queueSize),
                new NamedThreadFactory("guardcat-server-metrics-fetchService"), handler);
        fetchMachineWorker = new ThreadPoolExecutor(cores, cores,
                keepAliveTime, TimeUnit.MILLISECONDS, new ArrayBlockingQueue<>(queueSize),
                new NamedThreadFactory("guardcat-server-metrics-fetchWorker"), handler);
    }

    /**
     * 在 WEB 容器初始化完成后，启动拉取任务
     */
    @Override
    public void onApplicationEvent(WebServerInitializedEvent event) {
        start();
    }

    /**
     * 启动拉取任务
     */
    private void start() {
        fetchScheduleService.scheduleAtFixedRate(() -> {
            try {
                // 拉取全部APP指标信息
                fetchAllApp();
            } catch (Exception e) {
                log.info("拉取全部数据错误: ", e);
            }
        }, 500, serverProperties.getFetchMachineInterval(), TimeUnit.MILLISECONDS);
    }

    /**
     * 拉取全部应用指标信息, 从 {@link AppManager#getAppNames()} 中获取所有通过心跳请求注册的应用, 再通过 fetchAppService 线程池拉取该应用的全部指标信息
     */
    private void fetchAllApp() {
        if (appManager == null) {
            return;
        }
        // 获取到所有的APP
        Set<String> appNames = appManager.getAppNames();
        if (CollUtil.isEmpty(appNames)) {
            return;
        }
        // 遍历APP,查询该APP的指标信息
        for (final String appName : appNames) {
            fetchAppService.submit(() -> {
                try {
                    doFetchAppMachineMetric(appName);
                } catch (Exception e) {
                    log.error("拉取数据错误: ", e);
                }
            });
        }
    }

    /**
     * 通过 {@link AppManager#getMachines(String)} 指定应用的全部机器信息, 逐一拉取机器对应指标
     *
     * @param appName 应用名称
     */
    private void doFetchAppMachineMetric(final String appName) {
        // 如果机器不在线, 则跳过
        for (MachineInfo machine : appManager.getMachines(appName)) {
            if (!machine.isOnline()) {
                continue;
            }

            // 如果不是生产环境, 则跳过, 防止拉取过多无用数据
//            if (machine.isNotProd()) {
//                continue;
//            }

            /**
             * 构建具体的线程, 交由 fetchMachineWorker 线程池拉取
             */
            List<MetricFetchJob<?>> tasks = MetricFetchJobFactory.build(machine);

            /*
             * 创建拉取任务，任务数 ≈ 当前机器数 * 需要拉取的指标种类
             * 由于机器注册的时间不相同，所以所有机器不会在同一时间拉取指标，而是不均匀的分布在5秒内拉取的
             * 机器数越多，服务的负载就越大，为了降低负载可以调大 FETCH_MACHINE_INTERVAL_MS 的设置，但会使每个指标的间隔变长
             */
            for (MetricFetchJob<?> task : tasks) {
                try {
                    fetchMachineWorker.submit(task);
                } catch (Exception e) {
                    e.printStackTrace();
                    log.info("拉取数据错误:appName:{}, taskName:{}", appName, task.getClass().getName());
                }
            }
//            log.info("==============================================================================================================================");
        }


    }
}
















































